<?php
/**
 *+------------------
 * Lflow
 *+------------------
 * Copyright (c) 2023~2030 gitee.com/liu_guan_qing All rights reserved.本版权不可删除，侵权必究
 *+------------------
 * Author: Mr.April(405784684@qq.com)
 *+------------------
 */
declare (strict_types=1);

namespace lflow\workflow;

use lflow\assignment\Assignment;
use lflow\entity\Order;
use lflow\entity\Process;
use lflow\helper\AssertHelper;
use lflow\helper\ClassHelper;
use lflow\helper\DateHelper;
use lflow\helper\JsonHelper;
use lflow\helper\ObjectHelper;
use lflow\helper\StringHelper;
use lflow\IManagerService;
use lflow\IOrderService;
use lflow\IProcessService;
use lflow\IQueryService;
use lflow\ITaskService;
use lflow\Load;
use lflow\Logger;
use lflow\model\TaskModel;
use lflow\model\TransitionModel;
use lflow\WorkFlowEngineAlias;
use lflow\WorkFlowException;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ReflectionClass;
use RegexIterator;
use think\facade\App;

/**
 * 基本流程引擎实现类
 *
 * @author Mr.April
 * @since  1.0
 */
class WorkFlowEngine implements WorkFlowEngineAlias
{

    /**
     * 流程定义业务类
     */
    protected IProcessService $processService;

    /**
     * 流程实例业务类
     */
    protected IOrderService $orderService;

    /**
     * 任务业务类
     */
    protected ITaskService $taskService;

    /**
     * 查询业务类
     */
    protected IQueryService $queryService;

    /**
     * 管理业务类
     */
    protected IManagerService $managerService;

    /**
     * @var \lflow\workflow\Execution
     */
    protected Execution $execution;

    public function __construct()
    {
        $this->processService = new ProcessService();
        $this->orderService   = new OrderService();
        $this->taskService    = new TaskService();
        $this->queryService   = new QueryService();
        $this->managerService = new ManagerService();
        $this->execution      = new Execution();
    }

    public function execution(): Execution
    {
        return $this->execution;
    }

    public function process(): IProcessService
    {
        AssertHelper::notNull($this->processService);
        return $this->processService;
    }

    public function order(): IOrderService
    {
        AssertHelper::notNull($this->orderService);
        return $this->orderService;
    }

    public function task(): ITaskService
    {
        AssertHelper::notNull($this->taskService);
        return $this->taskService;
    }

    public function query(): QueryService
    {
        AssertHelper::notNull($this->queryService);
        return $this->queryService;
    }

    public function manager(): ManagerService
    {
        AssertHelper::notNull($this->managerService);
        return $this->managerService;
    }

    public function startInstanceById(string $id, string $operator, string $args): Order
    {
        $process = $this->processService->getProcessById($id);
        $this->processService->check($process, $id);
        return $this->startProcess($process, $operator, $args);
    }

    private function startProcess(Process $process, string $operator, string $args): Order
    {
        try {
            //$param=> [0=process 1=operator 2=args 3=parentId 4=parentNodeName 5=priority]
            $Execute = $this->execute($process, $operator, $args, $parentId = '', $parentNodeName = '', $parentNodeName = 1);
            $start   = $process->getModel()->getStart();
            AssertHelper::notNull($process->getModel()->getStart(), "流程定义[name=" . $process->getDisplayName() . ", version=" . $process->getVersion() . "]没有开始节点");
            $start->exec($Execute);
            return $Execute->getOrder();
        } catch (WorkFlowException $e) {
            throw new WorkFlowException($e->getMessage());
        }
    }

    public function startInstanceByExecution(Execution $execution): Order
    {
        $process = $execution->getProcess();
        $start   = $process->__get('model')->getStart();
        AssertHelper::notNull($start, "流程定义[id=" . $process->__get('id') . "]没有开始节点");
        //$param=> [0=process 1=operator 2=args 3=parentId 4=parentNodeName 5=priority]
        $current = $this->execute($process, $execution->getOperator(), $execution->getArgs(), $execution->getParentOrder()->getId(), $execution->getParentNodeName());
        $start->execute($current);
        return $current->getOrder();
    }

    private function executeOne(process $process, string $operator, string|object $args, string $parentId = '', string $parentNodeName = '', string|int $priority = 1): Execution|null
    {
        //创建流程实例
        $order = $this->orderService->createOrder($process, $operator, $args, $parentId, $parentNodeName, $priority);
        if (Logger::isDebugEnabled()) {
            Logger::debug("创建流程实例对象:" . json_encode($order));
        }
        $current = new Execution();
        $current->execution($this, $process, $order, $args);
        $current->setOperator($operator);
        return $current;
    }

    public function executeTwo(string $taskId, string $operator, string $args): Execution|null
    {
        $task = $this->taskService->complete($taskId, $operator, $args);
        if (Logger::isDebugEnabled()) {
            Logger::debug("任务[taskId=" . $taskId . "]已完成");
        }
        $args = JsonHelper::fromJson($args);
        if ($args == null) {
            $args = new \stdClass();
        }
        $order = $this->query()->getOrder($task->getOrderId());
        AssertHelper::notNull($order, "指定的流程实例[id=" . $task->getOrderId() . "]已完成或不存在");
        $order->setLastUpdator($operator);
        $order->setLastUpdateTime(DateHelper::getTime());
        $this->order()->updateOrder($order);
        //协办任务完成不产生执行对象
        if (!$task->isMajor()) {
            return null;
        }
        //参数处理
        $orderMaps = $order->getVariable();
        if ($orderMaps != null) {
            $args = json_encode(ObjectHelper::putAll($args, JsonHelper::fromJson($orderMaps)));
        }
        $process   = $this->processService->getProcessById($order->getProcessId());
        $execution = new Execution();
        $execution->execution($this, $process, $order, $args);
        $execution->setOperator($operator);
        $execution->setTask($task);
        return $execution;
    }

    public function executeTask(string $taskId, string $operator, string $args): void
    {
        try {

            //完成任务，并且构造执行对象
            $execution = $this->execute($taskId, $operator, $args);
            if ($execution == null) {
                return;
            }
            $model = $execution->getProcess()->getModel();
            if (!empty($model)) {
                $nodeModel = $model->getNode($execution->getTask()->getTaskName());
                //将执行对象交给该任务对应的节点模型执行
                $nodeModel->execute($execution);
            }
        } catch (WorkFlowException $e) {
            throw new WorkFlowException($e->getMessage());
        }
    }

    public function executeAndJumpTask(string $taskId, string $operator, string $args, string $nodeName): array
    {
        try {
            $execution = $this->execute($taskId, $operator, $args);
            if (empty($execution)) return [];
            $model = $execution->getProcess()->getModel();
            AssertHelper::notNull($model, "当前任务未找到流程定义模型");
            if (StringHelper::isEmpty($nodeName)) {
                $newTask = $this->task()->rejectTask($model, $execution->getTask());
                $execution->addTask($newTask);
            } else {
                $nodeModel = $model->getNode($nodeName);
                AssertHelper:: notNull($nodeModel, "根据节点名称[" . $nodeName . "]无法找到节点模型");
                //动态创建转移对象，由转移对象执行execution实例
                $tm = new TransitionModel();
                $tm->setTarget($nodeModel);
                $tm->setEnabled(true);
                $tm->execute($execution);
            }
            return $execution->getTasks();
        } catch (WorkFlowException $e) {
            throw new WorkFlowException($e->getMessage());
        }
    }

    public function createFreeTask(string $orderId, string $operator, string $args, TaskModel $model): array
    {
        try {
            $order = (new Order())->find($orderId);
            AssertHelper::notNull($order, "指定的流程实例[id=" . $orderId . "]已完成或不存在");
            $order->__set('operator', $operator);
            $order->__set('lastUpdateTime', DateHelper:: getTime());
            $process   = $this->processService->getProcessById($order->__get('processId'));
            $execution = $this->execution;
            $execution->execution($this, $process, $order, $args);
            $execution->setOperator($operator);
            $this->taskService->createTask($model, $execution);
            return [];
        } catch (WorkFlowException $e) {
            throw new WorkFlowException($e->getMessage());
        }
    }

    public function execute(): Execution|null
    {
        try {
            $param    = func_get_args();//获得参数数组
            $numParam = func_num_args();//获得参数数量
            switch ($numParam) {
                case 6:
                    //启动并创建流程实例
                    //$param=> [0=process 1=operator 2=args 3=parentId 4=parentNodeName 5=priority]
                    $result = $this->executeOne($param[0], $param[1], $param[2], $param[3], $param[4], $param[5]);
                    break;
                default:
                    //执行task任务
                    //$param=>[0=taskId,  1=operator, 2=args]
                    $result = $this->executeTwo($param[0], $param[1], $param[2]);
                    break;
            }

            return $result;
        } catch (WorkFlowException $e) {
            throw new WorkFlowException($e->getMessage());
        }
    }

    public function startInstanceByName(string $name, int $version, string $operator, string $args): Order
    {
        try {
            $process = $this->process()->getProcessByVersion($name, $version);
            $this->process()->check($process, $name);
            return $this->startProcess($process, $operator, $args);
        } catch (WorkFlowException $e) {
            throw new WorkFlowException($e->getMessage());
        }
    }

}
